/*
 * File:   main.cpp
 * Author: haoliang.donghl
 *
 * Created on 2009年7月21日, 下午1:54
 */
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#include "assign.h"
#include "namemap.h"

#define SIZE sizeof(struct sockaddr_in)

int xsockfd, xEngineSock;

char **appNames = 0;
char *LOCAL_IP_ADDRESS = "10.5.253.56";
int LOCAL_PORT = 2345;

/*
 * 创建socket连接
 */
void createSocket(int& sockfd, const char* ip, int port){
    struct sockaddr_in server = {AF_INET, htons(port), INADDR_ANY};
    int optval = 1;

    server.sin_addr.s_addr = inet_addr(ip) ;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
        perror("socket call failed");
        exit(1);
    }
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0){
        perror("socket reuse");
        close(sockfd);
        exit(1);
    }
    if (bind(sockfd, (struct sockaddr *)&server, SIZE)== -1){
        //绑定
        perror("bind call error");
        close(sockfd);
        exit(1);
    }
}

/*
 * 从字符串中获取获取名称为paraName的value
 * 参数：offset，字符串的偏移量
 *       result即名称为paraName的value，用于返回
 * 返回：处理完之后新的offset
 */
int getStringParameter(char* msg, int offset, const char* paraName, char* result){
    int i, p;
    i = 0;
    while (paraName[i] != 0){
        if (paraName[i] != msg[offset + i]){
            return -1;
        }
        i++;
    }
    i++;
    p = 0;
    while (msg[offset + i] != '&' && msg[offset + i] != '\0'
        && msg[offset + i] != '\r' && msg[offset + i] != '\n'){
        result[p] = msg[offset + i];
        i++;
        p++;
    }
    result[p] = '\0';
    return offset + i + 1;
}

/*
 * 从字符串中获取获取名称为paraName的value，并转成整形输出
 * 参数：offset，字符串的偏移量
 *       result即名称为paraName的value，用于返回
 * 返回：处理完之后新的offset
 */
int getIntParameter(char* msg, int offset, const char* paraName, int& result){
    char temp[128];
    int ret;
    ret = getStringParameter(msg, offset, paraName, temp);
    result = atoi(temp);
    return ret;
}

/*
 * 将字符串包装成Erlang可识别的格式
 * 参数：msg 欲包装的字符串
 *       bytes 包装完后的包
 * 返回：包装完后的数据长度
 */
int packetErlangData(const char* msg, char* bytes){
    printf("SEND MESSAGE: %s", msg);
    int l = strlen(msg);
    bytes[0] = '\0';
    bytes[1] = '\0';
    bytes[2] = '\0';
    bytes[3] = (char)l;
    memcpy(&bytes[4], msg, l);
    return l + 4;
}

/*
 * 接受Erlang传来的数据，并将数据转成C可是别的格式
 * 参数：rev 用于返回收到的数据
 */
int receive(int sockfd, char * rev){
    char temp[8];
    int p = 0;
    int ret;
    do{
        ret = recv(sockfd, &rev[p], 1, 0);
        if (ret == 0) return -1;
        if (rev[p] == '\0'){
            ret = recv(sockfd, temp, 3, 0);
            if (ret == 0) return -1;
        }else{
            p++;
        }
    }while(rev[p - 1] != '\n');
    rev[p] = '\0';

    printf("RECEIVE MESSAGE: %s \n", rev);
    return 0;
}

/*
 * 处理请求App状态的返回信息
 */
void recvMsgType2(int appId, char * rev){
    int i;
    int msgType, machineNum;
    int offset = 0;
    char machineName[64], appName[64];
    int machineId, state, intLoad;
    float load;
    offset = getIntParameter(rev, offset, "msgtype", msgType);
    offset = getIntParameter(rev, offset, "appid", appId);
    offset = getStringParameter(rev, offset, "appname", appName);
    offset = getIntParameter(rev, offset, "machinenum", machineNum);
    appId -= 1;
    strcpy(appNames[appId], appName);
    for (i = 0; i < machineNum; i++){
        offset = getStringParameter(rev, offset, "machinename", machineName);
        offset = getIntParameter(rev, offset, "state", state);
        offset = getIntParameter(rev, offset, "load", intLoad);
        machineId = nameToId(machineName);
        load = (float)intLoad / 100;

        stateM[appId][machineId] = load;
        assignM[appId][machineId] = state - 1;
    }
    appSizeV[appId] = 10;

}
/*
 * 算法模块请求每个App的状态
 * 参数：appId 所请求的App的编号
 */
void sendMsgType2(int appId){
    char str[255];
    char sev[255];
    char rev[4096];
    int l;
    sprintf(str, "msgtype=2&appid=%d\r\n", appId + 1);
    l = packetErlangData(str, sev);
    send(xEngineSock, sev, l, 0);
    receive(xEngineSock, rev);
    recvMsgType2(appId, rev);
}

/*
 * 通知计算完毕
 */
void processMsgType3(){
    char str[4096];
    char sev[4096];
    char rev[255];
    int l;
    sprintf(str, "msgtype=3&machinenum=%d&appnum=%d\r\n", numOfPM, numOfApp);
    l = packetErlangData(str, sev);
    send(xEngineSock, sev, l, 0);
    receive(xEngineSock, rev);
}

/*
 * 调度模块向算法模块发起调度请求，处理这部分内容
 * 参数：msg 所收的请求字符串
 *      offset 字符串去除前面已经处理过的字符的偏移量
 */
void processMsgType1(char* msg, int offset){
    int i, l;
    char sev[255];
    offset = getIntParameter(msg, offset, "machinenum", numOfPM);
    offset = getIntParameter(msg, offset, "appnum", numOfApp);

    destoryMatrix();
    initMatrix();
    if (appNames){
        for (i = 0; i < numOfApp; i++){
            delete appNames[i];
        }
        delete appNames;
    }
    appNames = new char*[numOfApp];
    for (i = 0; i < numOfApp; i++){
        appNames[i] = new char[64];
    }

    l = packetErlangData("msgtype=1&response=ok\r\n", sev);
    send(xsockfd, sev, l, 0);

    clearNamemap();
    for (i = 0; i < numOfApp; i++){
        sendMsgType2(i);
    }

    calculateAppNeeds();
    //TODO:存入获取的数据
    //state[appId][PMId]
    //appNeeds[appId]
    //appName = appNames[appId]
    //PMName = idToName(PMId);
    //PMId = nameToid(PMName);

    processMsgType3();
}

/*
 * 接收到msgtype=4的消息，处理该类型的消息
 */
void processMsgType4(char* msg, int offset){
    int appId, state;
    char temp[64];
    char appName[64];
    char str[4096];
    char sev[4096];
    int i, l;
    offset = getStringParameter(msg, offset, "appname", appName);
    for (i = 0; i < numOfApp; i++){
        if (strcmp(appName, appNames[i]) == 0) break;
    }
    appId = i;
    offset = getIntParameter(msg, offset, "state", state);
    sprintf(str, "msgtype=4&appname=%s&machinenum=%d", appNames[appId], numOfApp);
    strcat(str, "%");
    for (i = 0; i < numOfPM; i++){
        if (assignM[appId][i] + 1 == state){
            idToName(i, temp);
            strcat(str, temp);
            strcat(str, "#");
        }
    }
    strcat(str,"%response=ok\r\n");
    l = packetErlangData(str, sev);
    send(xEngineSock, sev, l, 0);
}

/*
 * 收到了请求字符串，按照字符串的msgtype开始处理
 * 参数：msg 所收的请求字符串
 */
void processMessage(char* msg){
    int offset = 0;
    int msgtype;
    offset = getIntParameter(msg, offset, "msgtype", msgtype);
    switch(msgtype){
        case 1:
            processMsgType1(msg, offset);
            break;
        case 4:
            processMsgType4(msg, offset);
            break;
    }
}

/*
 * 开始监听端口
 */
void listenport(int& sockfd, int& newsockfd){
    char rev[4096];
    int ret;
    ret = 0;
    while(true){
        if(listen(sockfd, 5) == -1){
            perror("listen call error");
            close(xsockfd);
            exit(1);
        }
        printf("forecast-xengine start listen....\n");
        if((newsockfd = accept(sockfd, NULL, NULL)) == -1){
            perror("accept call error");
            continue;
        }else{
            printf("connected...\n");
            do{
                ret = receive(newsockfd, rev);
                if (ret == 0) processMessage(rev);
            }while (ret == 0);
            printf("disconnected...\n");
        }
    }
}

/*
 * 主函数，创建连接并监听
 */
int main(int argc, char** argv) {
    createSocket(xsockfd, LOCAL_IP_ADDRESS, LOCAL_PORT);
    listenport(xsockfd, xEngineSock);
    return (EXIT_SUCCESS);
}


